package cn.ucox.web.ms.redis;

import cn.ucox.web.ms.config.DataExchangeConfig.DexRedisConfig;
import cn.ucox.web.ms.util.BeanUtil;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.Tuple;

import java.util.*;
import java.util.stream.Collectors;


/**
 * Redis连接客户端
 *
 * @author chenw
 * @create 2017-02-28 10:32
 * @email javacspring@gmail.com
 */
public class RedisClient {

    private static final Logger logger = LoggerFactory.getLogger(RedisClient.class);

    private static JedisPool jedisPool = null;
    private static RedisClient instance = new RedisClient();

    private RedisClient() {

    }

    public static RedisClient instance() {
        return instance;
    }

    public synchronized void init(DexRedisConfig redisConfig) {
        if (null != jedisPool)
            return;
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(redisConfig.getMaxTotal());
            config.setMaxIdle(redisConfig.getMaxIdle());
            config.setMinIdle(redisConfig.getMinIdle());
            config.setTestOnBorrow(redisConfig.isTestOnBorrow());
            if (null != redisConfig.getPassword() && redisConfig.getPassword().trim().length() > 0)
                jedisPool = new JedisPool(config, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout(), redisConfig.getPassword());
            jedisPool = new JedisPool(config, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout());
            logger.debug("RedisClient初始化成功");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取Jedis实例
     *
     * @return
     */
    public synchronized Jedis getJedis() {
        try {
            if (jedisPool != null) {
                return jedisPool.getResource();
            } else {
                return null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 释放jedis资源
     *
     * @param jedis
     */
    public static void close(final Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

    /**
     * 向缓存中保存字符内容
     *
     * @param key   缓存key
     * @param value 缓存值
     * @return true保存成功
     */
    public String save(String key, String value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                return jedis.set(key, value);
            return "";
        } finally {
            close(jedis);
        }
    }

    /**
     * 保存对象，并设置过期时间
     *
     * @param key           缓存key
     * @param value         缓存值
     * @param expireSeconds 过期时间(秒)
     * @return true保存成功
     */
    public String save(String key, String value, int expireSeconds) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                return jedis.setex(key, expireSeconds, value);
            return "";
        } finally {
            close(jedis);
        }

    }

    /**
     * 判断指定key是否存在
     *
     * @param key 指定key
     * @return true存在 false不存在
     */
    public boolean exists(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                return jedis.exists(key);
            return false;
        } finally {
            close(jedis);
        }
    }

    /**
     * 获取指定key缓存过期时间
     *
     * @param key 指定key
     * @return 过期时间
     */
    public long ttl(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                return jedis.ttl(key);
            return 0;
        } finally {
            close(jedis);
        }
    }

    /**
     * 删除指定key缓存
     *
     * @param key 指定key
     */
    public long delete(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                return jedis.del(key);
            return 0;
        } finally {
            close(jedis);
        }
    }

    /**
     * 保存对象至Redis缓存
     *
     * @param key    缓存KEY
     * @param object 待保存对象
     * @return
     */
    public <T> Object hmset(String key, T object) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                String rt = jedis.hmset(key, BeanUtil.bean2Map(object));

                return object;
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 创建hash集合，如果已存在则不创建
     *
     * @param key hash集合KEY
     * @param map 集合数据
     * @return
     */
    public String hmsetx(byte[] key, Map<byte[], byte[]> map) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                if (!jedis.exists(key)) {
                    return jedis.hmset(key, map);
                }
            return "";
        } finally {
            close(jedis);
        }
    }

    public String hmsetx(String key, String... keyValuePairs) {
        if (null == key)
            throw new RuntimeException("key is null");
        if (null == keyValuePairs || 2 > keyValuePairs.length || 0 != keyValuePairs.length % 2) {
            throw new RuntimeException("key value pairs is invalidate");
        }
        int len = keyValuePairs.length;
        HashMap<String, String> values = new HashMap<>(keyValuePairs.length / 2);
        for (int i = 0; i < len; i = i + 2) {
            values.put(keyValuePairs[i], keyValuePairs[i + 1]);
        }
        Jedis jedis = getJedis();
        try {
            if (null != jedis)
                if (!jedis.exists(key)) {
                    return jedis.hmset(key, values);
                }
            return "";
        } finally {
            close(jedis);
        }
    }

    /**
     * 从缓存中读取对象
     *
     * @param key   对象KEY
     * @param clazz 对象所属类
     * @param <T>   返回结果类型
     * @return 缓存中反序列化对象
     */
    public <T> T getObject(String key, T clazz) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                Map<String, String> result = jedis.hgetAll(key);
                return BeanUtil.map2Bean(result, clazz);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 指定key步增
     *
     * @param key 步增key
     * @return 指定key步增
     */
    public long increase(String key, long deltaValue) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.incrBy(key, deltaValue);
            }
            return 0;
        } finally {
            close(jedis);
        }
    }

    /**
     * 指定key步增1
     *
     * @param key 步增key
     * @return 指定key步增
     */
    public long increase(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                jedis.incr(key);
            }
            return 0;
        } finally {
            close(jedis);
        }
    }

    /**
     * 更新缓存对象属性
     *
     * @param key      缓存对象KEY
     * @param property 缓存对象属性名称
     * @param value    更新值
     * @return 更新结果 true更新成功 false更新失败
     */
    public boolean update(String key, String property, Object value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                if (value instanceof String) {
                    jedis.hset(key.getBytes(), property.getBytes(), value.toString().getBytes());
                    return true;
                } else if (value instanceof Integer
                        || value instanceof Long
                        || value instanceof Double
                        || value instanceof Float
                        || value instanceof Boolean) {
                    jedis.hset(key.getBytes(), property.getBytes(), String.valueOf(value).getBytes());
                    return true;
                } else {
                    jedis.hset(key.getBytes(), property.getBytes(), JSON.toJSONBytes(value));
                    return false;
                }
            }
            return false;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从缓存中读取缓存值<br>返回值为基础数据类型或JSON字符串
     *
     * @param key 缓存key
     * @return 返回值 返回值为基础数据类型或JSON字符串
     */
    public String get(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.get(key);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从列表左侧插入新值至缓存列表
     *
     * @param key    缓存列表key值
     * @param values 新插入对象
     */
    public boolean leftPush(String key, String... values) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                jedis.lpush(key, values);
            }
            return false;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从列队尾部取出数据
     *
     * @param key 缓存key
     * @return String 返回值
     */
    public String rightPop(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.rpop(key);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从队列中右侧弹出最后一个元素
     *
     * @param key 队列名称
     * @return 出列数据
     */
    public String leftPop(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.lpop(key);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 批量将对象存储指定Set集合中
     *
     * @param key    集合key
     * @param values 缓存对象
     * @return true存储成功
     */
    public Long add(String key, String... values) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.sadd(key, values);
            }
            return 0L;
        } finally {
            close(jedis);
        }
    }

    public String hmset(String key, Map<String, String> values) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hmset(key, values);
            }
            return "";
        } finally {
            close(jedis);
        }
    }

    public Map<String, String> hgetAll(String key) {
        Map<byte[], byte[]> rt = hgetAll(key.getBytes());
        if (null == rt || rt.isEmpty())
            return null;
        else {
            return rt.entrySet()
                    .stream()
                    .collect(Collectors
                            .toMap(entry -> new String(entry.getKey()),
                                    entry -> new String(entry.getValue())));
        }
    }

    public long hset(String key, String field, String value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hset(key, field, value);
            }
            return 0L;
        } finally {
            close(jedis);
        }
    }

    public long hset(byte[] key, byte[] field, byte[] value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hset(key, field, value);
            }
            return 0L;
        } finally {
            close(jedis);
        }
    }

    public long hincrBy(String key, String field, long value) {
        return hincrBy(key.getBytes(), field.getBytes(), value);
    }

    public long hincrBy(byte[] key, byte[] field, long value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hincrBy(key, field, value);
            }
            return 0L;
        } finally {
            close(jedis);
        }

    }

    public Map<byte[], byte[]> hgetAll(byte[] key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hgetAll(key);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public long lpush(String key, String... values) {
        byte[][] val = Arrays.stream(values).map(String::getBytes).toArray(byte[][]::new);
        return lpush(key.getBytes(), val);
    }

    public long lpush(byte[] key, byte[]... values) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.lpush(key, values);
            }
            return 0L;
        } finally {
            close(jedis);
        }

    }

    public long hdel(byte[] key, byte[][] values) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hdel(key, values);
            }
            return 0L;
        } finally {
            close(jedis);
        }
    }

    public String brpoplpush(String source, String destination, int timeout) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.brpoplpush(source, destination, timeout);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public byte[] brpoplpush(byte[] source, byte[] destination, int timeout) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.brpoplpush(source, destination, timeout);
            }
            return new byte[0];
        } finally {
            close(jedis);
        }
    }

    public List<String> lrange(String key, long start, long end) {
        return lrange(key.getBytes(), start, end)
                .stream()
                .map(String::new)
                .collect(Collectors.toList());
    }

    public List<byte[]> lrange(byte[] key, long start, long end) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.lrange(key, start, end);

            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从列表尾部(右侧)删除指定长度元素
     *
     * @param key    队列名称
     * @param length 删除元素个数
     */
    public String lrtrim(byte[] key, long length) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.ltrim(key, 0, -1 - length);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从列表头部(左侧)删除指定长度元素
     *
     * @param key    队列名称
     * @param length 删除元素个数
     */
    public String lltrim(byte[] key, long length) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.ltrim(key, length, -1);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    /**
     * 从列表中探测元素，不删除
     *
     * @param key   列表集合名
     * @param start 开始元素 -1为正序最后一个元素 0为正序第1个元素
     * @param end   获取元素范围结束值
     * @return 指定范围元素子集
     */
    public byte[] peek(byte[] key, int start, int end) {
        List<byte[]> result = lrange(key, start, end);
        if (null != result && result.size() > 0) {
            return result.get(0);
        } else {
            return new byte[0];
        }
    }

    /**
     * 向有序集合中添加元素
     *
     * @param key   集合KEY
     * @param score 排序字段值
     * @param value 集合元素
     * @return 1 新增成功 0元素已存在，更新成功
     */
    public long zadd(String key, double score, String value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.zadd(key, score, value);
            }
            return -1;
        } finally {
            close(jedis);
        }
    }

    public String hget(String key, String field) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis) {
                return jedis.hget(key, field);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public List<String> hmget(String key, String... fields) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != fields && fields.length > 0) {
                return jedis.hmget(key, fields);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public Map<String, String> getMap(String key, String... fields) {
        List<String> result = hmget(key, fields);
        if (null != result && 0 < result.size() && fields.length == result.size()) {
            return new HashMap<String, String>(result.size()) {{
                int index = 0;
                for (String rt : result) {
                    put(fields[index], rt);
                    index++;
                }
            }};
        }
        return null;
    }

    public Set<String> zrange(String key, int start, int length) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.zrange(key, start, start + length);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public Set<Tuple> zrangeWithScore(String key, long start, long end) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.zrangeWithScores(key, start, end);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public Set<Tuple> zrangeWithScore(String key, long start, int length) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.zrangeWithScores(key, start, start + length);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public Set<Tuple> zrangeByScoreWithScores(String key, double min, double max) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.zrangeByScoreWithScores(key, min, max);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

    public boolean hexists(String key, String field) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.hexists(key, field);
            }
            return false;
        } finally {
            close(jedis);
        }
    }

    public long zremrangeByScore(String key, double start, double end) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.zremrangeByScore(key, start, end);
            }
            return 0;
        } finally {
            close(jedis);
        }
    }

    public long llen(String key) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.llen(key);
            }
            return 0;
        } finally {
            close(jedis);
        }
    }

    public long lrem(String key, long count, String value) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != key) {
                return jedis.lrem(key,count,value);
            }
            return 0;
        } finally {
            close(jedis);
        }
    }

    public Set<String> keys(String pattern) {
        Jedis jedis = getJedis();
        try {
            if (null != jedis && null != pattern) {
                return jedis.keys(pattern);
            }
            return null;
        } finally {
            close(jedis);
        }
    }

}
